<template>
    <div class="m-p-6 batch">
        <el-row>
            <el-col :span="20">题目导入</el-col>
            <el-col :span="4" class="text-right">
                <el-button type="warning" @click="drawer = true">日志</el-button>
            </el-col>
        </el-row>
        <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="136px" status-icon>

            <el-form-item label="请选择题目模板：" prop="region">
                <el-select v-model="ruleForm.region" placeholder="请选择题目模板">
                    <el-option v-for="v in Object.keys(list)" :label="v" :value="v" />
                </el-select>
            </el-form-item>
            <el-form-item label="选择用户：" prop="userid">
                <el-select-v2 v-model="ruleForm.userid" filterable remote :options="options" placeholder="请输入用户名"
                    :remote-method="(str: string) => store.dispatch('actionusers', { _like: ['name', str] })" />
            </el-form-item>
            <el-form-item label="题目归属团队：" prop="team">
                <el-select-v2 :style="{ width: '100%' }" v-model="ruleForm.team" multiple filterable remote
                    placeholder="请输入团队名称"
                    :remote-method="(str: string) => store.dispatch('actionteam', { _like: ['name', str] })" clearable
                    :options="options2" />
            </el-form-item>
            <el-form-item label="请上传导入文件：">
                <el-upload action="/" :show-file-list="false" accept=".zip" :http-request="request" class="d-inline-block">
                    <el-button class="icon-button" type="primary">
                        <svg class="icon icon-right" aria-hidden="true">
                            <use xlink:href="#icon-icon_tianjiatimu_upload" />
                        </svg>
                        <span>+ 题目zip文件</span>
                    </el-button>
                </el-upload>&nbsp;
                <el-link href="https://cdn.hterobot.com/noipcode/problem_export_1670199879738.zip">下载示例</el-link>
            </el-form-item>
        </el-form>
        <el-table class="data2table" border :data="Object.keys(tabledata)" style="width: 100%" height="50vh">
            <el-table-column prop="user" label="文件名" :width="150">
                <template #default="{ $index, row }">
                    {{ row }}
                </template>
            </el-table-column>
            <el-table-column prop="questionid" label="题目ID" :width="100">
                <template #default="{ row }">
                    <span v-if="typeof tabledata[row] === 'string'">
                        数据错误
                    </span>
                    <span v-else>
                        {{ tabledata[row]?.problem?.problemId }}
                    </span>
                </template>
            </el-table-column>
            <el-table-column prop="questionbankid" label="题目名" :width="200">
                <template #default="{ row }">
                    <span v-if="typeof tabledata[row] === 'string'">
                        数据错误
                    </span>
                    <span v-else>
                        {{ tabledata[row]?.problem?.title }}
                    </span>

                </template>
            </el-table-column>
            <el-table-column prop="createdAt" label="测试点数" :width="100">
                <template #default="{ row }">
                    <span v-if="typeof tabledata[row] === 'string'">
                        数据错误
                    </span>
                    <span v-else>
                        {{ getsize(row) }}

                    </span>
                </template>
            </el-table-column>
            <el-table-column prop="question" label="原标签" :width="200">
                <template #default="{ row }">
                    <span v-if="typeof tabledata[row] === 'string'">
                        数据错误
                    </span>
                    <span v-else>
                        {{ tabledata[row]?.tags }}
                    </span>

                </template>
            </el-table-column>
            <el-table-column prop="type" label="导入标签">
                <template #default="{ row }">
                    <span v-if="typeof tabledata[row] === 'string'">
                        数据错误
                    </span>
                    <batchlabelVue v-else :tabledata="tabledata" :row="row" :label="label"></batchlabelVue>
                </template>
            </el-table-column>
            <el-table-column prop="result" label="操作" :width="72">
                <template #default="{ row }">
                    <span class="cu" @click="() => delete tabledata[row]">移除</span>
                </template>
            </el-table-column>
        </el-table>
        <div class="row m-mt-6">
            <div class="col-3">
                当前共有{{ Object.keys(tabledata).length }}题
            </div>

        </div>
        <div class="row m-mt-6">
            <div class="text-center col">
                <el-button type="primary" class="el-button5" :loading="loading"
                    @click="submitForm(ruleFormRef)">导入</el-button>
            </div>
        </div>
        <el-drawer v-model="drawer" title="导入日志" :with-header="false">
            <el-timeline>
                <el-timeline-item v-for="(activity, index) in log" :key="index" :timestamp="activity.timestamp">
                    {{ activity.content }}
                </el-timeline-item>
            </el-timeline>
        </el-drawer>
    </div>
</template>
  
<script lang="ts" setup>
import { computed, reactive, ref } from 'vue'
import { ElMessage, ElNotification, FormInstance, FormRules, UploadRequestHandler, dayjs } from 'element-plus'
import { updatezip } from '../../util/updateoss';
import JSZip from 'jszip';
import { v4 } from 'uuid'
import { useStore } from 'vuex';
import { Storekey } from '../../interface';
import batchlabelVue from './batchlabel.vue';
import Studycodemodel from '../../mysql_interface/studycodemodel';
import { forEach, groupBy, head, last } from 'lodash-es';
import { asynccreateorupdatespecialjudge, asynccreatequestionall, ossupload } from '../../api';
import { isOut } from '../../util/RegExp';
import OSS from 'ali-oss';
import { settestcasesscore } from '../../util/code';

const p = defineProps<{ isAdmin: boolean, selectquestion: Function }>()
const store = useStore(Storekey)
const label = computed(() => {
    return store.state.label?.rows || []
})

const drawer = ref(false)

const loading = ref(false)

const formSize = ref('default')
const ruleFormRef = ref<FormInstance>()
const ruleForm = reactive({
    name: 'Hello',
    region: '',
    team: [],
    userid: '',
    resource: false
})

interface Isamples {
    input: string;
    output: string
    groupNum: number

}

interface IcodeTemplates {
    code: string;
    language: string

}

interface BatchJSON {
    judgeMode?: 'default' | 'spj' | 'interactive',
    samples: Isamples[]
    tags: string[]
    tags2: number[]
    problem: {
        isRemote?: boolean
        problemId?: string
        description?: string
        source?: string
        title?: string
        timeLimit?: number
        memoryLimit?: number
        input?: string
        output?: string
        difficulty?: string
        examples?: string
        hint?: string
        isRemoveEndBlank?: string
        judgeCaseMode?: 'default' | 'subtask_lowest' | 'subtask_average'
        spjCode?: string
        spjLanguage?: string
    },
    codeTemplates?: IcodeTemplates[];
    userExtraFile?: {
        'testlib.h': boolean
    },
    judgeExtraFile?: {
        'testlib.h': boolean
    }
}

/**
 * 保存所有文件名
 */
const _zipdata = ref<string[]>([])
/**
 * 保存所有文件
 */
const _zipobj: JSZip.JSZipObject[] = []

/**
 * 保存所有json名
 */
const zipdata = ref<string[]>([])

const tabledata = ref<Record<string, BatchJSON | string>>({})

const list = {
    'CodeForces': 'CodeForces'
}


function getsize(str: string) {

    const _in = new RegExp(/\.in$/);
    const t = _zipdata.value.filter(t => {
        const l = t.split('/')
        return l[l.length - 2] == str.replace('.json', '') && _in.test(t)
    })
    return t.length
}


const allquestions = ref<Studycodemodel.Iquestion[]>([])


const request: UploadRequestHandler = async (file) => {
    const { size } = file.file;
    if (size > 1024 * 1024 * 1024) {
        ElMessage.error("上传文件过大，最大只能上传1GB");
        log.value.push({
            content: '上传文件过大，最大只能上传1GB',
            timestamp: dayjs().format('HH:mm:ss')
        })
        return;
    }
    log.value.push({
        content: '开始解压',
        timestamp: dayjs().format('HH:mm:ss')
    })
    ElNotification({
        title: "解压",
        message: "开始解压",
        type: "info",
    });

    const zip = await JSZip.loadAsync(file.file);
    zipdata.value = []
    _zipdata.value = []
    _zipobj.splice(0, Infinity)
    const jsondata: JSZip.JSZipObject[] = []
    zip.forEach((rp, z) => {
        _zipdata.value.push(rp);
        _zipobj.push(z)
        let { name } = z
        name = name.toLowerCase()
        const _in = new RegExp(/\.json$/);
        if (_in.test(name)) {
            jsondata.push(z)
            zipdata.value.push(name)
        }
    });
    tabledata.value = {}
    for (const v of jsondata) {
        const { name } = v
        const json = await v.async('text')
        try {
            const data: BatchJSON = JSON.parse(json)
            if (data.tags) {
                data.tags2 = label.value.filter(t => data.tags.includes(t.name)).map(f => f.ID)
            }
            tabledata.value[name] = data

        } catch {
            tabledata.value[name] = '无法解析json'
        }
    }

    const questions = await p.selectquestion({
        id: Object.values(tabledata.value).map(t => {
            if (typeof t !== 'string') {
                return t.problem.problemId
            }
        })
    })
    allquestions.value = questions.rows
    log.value.push({
        content: '解压成功',
        timestamp: dayjs().format('HH:mm:ss')
    })
    ElNotification({
        title: "解压",
        message: "解压成功",
        type: "success",
    });
}

const rules = reactive<FormRules>({
    resource: [
        {
            required: true,
            message: '请选择处理方式',
            trigger: 'change',
        }
    ],
    region: [
        {
            required: true,
            message: '请选择项目',
            trigger: 'change',
        },
    ],
    userid: [
        {
            required: true,
            message: '请选择用户',
            trigger: 'change',
        },
    ],
    team: [
        {
            required: true,
            message: '请选择团队',
            trigger: 'change',
        },
    ],
})

const log = ref<{ content: string, timestamp: string }[]>([])

const submitForm = async (formEl?: FormInstance) => {
    if (!formEl) return
    await formEl.validate(async (valid, fields) => {
        if (valid) {
            drawer.value = true
            log.value.push({
                content: '--------------------------------------------------------------------',
                timestamp: dayjs().format('HH:mm:ss')
            })
            if (Object.keys(tabledata.value).length) {
                loading.value = true
                const data: Studycodemodel.Iquestion[] = []
                const spj: Record<string, string> = {}
                const testcases: Record<string, JSZip.JSZipObject[]> = {}
                forEach(tabledata.value, (v, i) => {
                    if (typeof v !== 'string') {
                        const { problem } = v
                        const { problemId, spjCode, spjLanguage } = problem
                        const name = i.replace('.json', '')
                        const test = _zipobj.filter(t => t.name?.includes(i.replace('.json', '')))
                        testcases[name] = test
                        const exampletest: Studycodemodel.Itestcases[] = []
                        problem.examples = problem.examples?.replaceAll('input', 'span').replaceAll('output', 'small') || '<div></div>'
                        const div = document.createElement('div')
                        div.innerHTML = problem.examples
                        div.querySelectorAll('*').forEach(e => {
                            if (e.tagName === 'SPAN') {
                                exampletest.push({ input: e.innerHTML })
                            } else if (e.tagName === 'SMALL') {
                                exampletest[exampletest.length - 1].output = e.innerHTML
                            }
                        })
                        if (spjLanguage) {
                            if (spjCode && ['c', 'c++', 'cpp'].includes(spjLanguage.toLocaleLowerCase())) {
                                spj[name] = spjCode
                            }
                        }

                        const question: Studycodemodel.Iquestion = {
                            name: problem.title,
                            userid: ruleForm.userid,
                            type: '代码',
                            context: problem.description,
                            timelimit: problem.timeLimit,
                            difficulty: problem.difficulty ? Number(problem.difficulty) > 5 ? '5' : problem.difficulty : '1',
                            exampletest,
                            source: problem.source,
                            inputformat: problem.input,
                            outputformat: problem.output,
                            tips: problem.hint,
                            isremoveendblank: Boolean(problem.isRemoveEndBlank),
                            judgecasemode: problem.judgeCaseMode,
                            range: 'user',
                            isopen: true,
                            questionlabel: v.tags2.map(e => { return { labelid: e } }),
                            goldremarks: problemId,
                            dataform: ruleForm.region,
                            dataformid: name
                        }
                        data.push(question)

                    }
                })
                console.log(data)
                console.log(spj)
                console.log(testcases)
                if (Object.keys(spj).length) {
                    ElMessage.info('开始编译spj')
                    log.value.push({
                        content: '开始编译spj',
                        timestamp: dayjs().format('HH:mm:ss')
                    })
                    for (const v in spj) {
                        const s = spj[v]
                        try {
                            const judge = await asynccreateorupdatespecialjudge({
                                name: v,
                                context: s
                            })
                            const t = data.find(t => t.dataformid == v)
                            if (t) {
                                t.specialjudgeid = judge.ID
                                t.isspecialjudge = true
                            }
                        } catch (e) {
                            ElMessage.error(`题目为ID ${v} 的题目spj编译失败`)
                            log.value.push({
                                content: `题目为ID ${v} 的题目spj编译失败`,
                                timestamp: dayjs().format('HH:mm:ss')
                            })
                            return
                        }
                    }
                    ElMessage.info('spj编译完成')
                }
                if (Object.keys(testcases).length) {
                    ElMessage.info('开始上传测试点')
                    log.value.push({
                        content: `开始上传测试点`,
                        timestamp: dayjs().format('HH:mm:ss')
                    })

                    const res: any = await ossupload()
                    const client = new OSS({
                        region: "oss-cn-qingdao",
                        accessKeyId: res.accessKeyId,
                        accessKeySecret: res.accessKeySecret,
                        stsToken: res.securityToken,
                        bucket: "cdn-hterobot"
                    });
                    for (const v in testcases) {
                        const t = testcases[v]
                        const uu = v4()
                        const obj: {
                            [key: string]: Studycodemodel.Itestcases;
                        } = {};
                        for (const iterator of t) {
                            let { name } = iterator
                            name = last(name.toLowerCase().split('/')) || ''
                            const _in = new RegExp(/\.in$/);
                            const out = isOut;
                            if (_in.test(name)) {
                                const files = await iterator.async("blob")
                                const url = `noip/${uu}/${name}`;
                                await client.put(url, files);
                                const text = `https://cdn.hterobot.com/${url}`
                                const tmp = obj[name.replace(_in, "")];
                                if (tmp) {
                                    obj[name.replace(_in, "")].input = text;
                                } else {
                                    obj[name.replace(_in, "")] = {
                                        input: text,
                                        output: undefined,
                                        type: 'url',
                                        isshow: true,
                                    };
                                }
                            }
                            if (out.test(name)) {
                                const files = await iterator.async("blob")
                                const url = `noip/${uu}/${name}`;
                                await client.put(url, files);
                                const text = `https://cdn.hterobot.com/${url}`
                                const tmp = obj[name.replace(out, "")];
                                if (tmp) {
                                    obj[name.replace(out, "")].output = text;
                                } else {
                                    obj[name.replace(out, "")] = {
                                        input: undefined,
                                        output: text,
                                        type: 'url',
                                        isshow: true,

                                    };
                                }
                            }
                        }
                        const ques = data.find(t => t.dataformid == v)
                        if (ques) {
                            ques.testcases = settestcasesscore(Object.values(obj))
                        }
                    }
                }

                asynccreatequestionall({
                    userid: ruleForm.userid,
                    team: ruleForm.team,
                    resource: ruleForm.resource,
                    question: data
                }).then(e => {
                    ElMessage.success(`添加成功${e.filter(t => typeof t === 'object').length}个`)
                    log.value.push({
                        content: `添加成功${e.filter(t => typeof t === 'object').length}个`,
                        timestamp: dayjs().format('HH:mm:ss')
                    })

                    loading.value = false

                }).catch(f => {
                    ElMessage.error(f)
                    log.value.push(f)

                    loading.value = false

                })

            } else {
                ElMessage.error('请添加文件')
            }



        } else {
            console.log('error submit!', fields)
        }
    })
}

const resetForm = (formEl: FormInstance | undefined) => {
    if (!formEl) return
    formEl.resetFields()
}

const options = computed(() => {
    return store.state.users?.rows?.map(e => {
        return {
            label: e.name,
            value: e.ID
        }
    }) || []
})

const options2 = computed(() => {
    return store.state.team?.rows?.map(e => {
        return {
            label: e.name,
            value: e.ID
        }
    }) || []
})
</script>

<style scoped>
.batch {
    background-color: var(--color-f5);
    min-height: 80vh;
}

.el-button5 {
    width: 132px;
    height: 48px;
    font-size: 18px;
    --el-button-bg-color: var(--color-43);
    --el-button-border-color: var(--color-43);
    --el-button-font-color: #fff;
}

.batch-header {
    font-weight: 700;
    font-size: 18px;
    margin-bottom: 32px;
}


.data2table {
    --el-table-border: #c5c5c5;
    --el-table-header-text-color: var(--color-43);
    --el-table-text-color: var(--color-43);
    --el-table-bg-color: var(--color-f5);
    --el-table-tr-bg-color: var(--color-f5);
    --el-table-header-bg-color: var(--color-f5);
}
</style>
